#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "select_svr.hpp"


int main()
{
    //创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sock < 0)
    {
        perror("socket");
        return 0;
    }

    //绑定地址信息
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(28989);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0"); //0.0.0.0表示本地所有的网卡地址

    int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0)
    {
        perror("bind");
        return 0;
    }

    //监听
    ret = listen(listen_sock, 1);// 1表示内核中已完成连接队列的大小，决定了服务端的并发连接数
    if(ret < 0)
    {
        perror("listen");
        return 0;
    }


    //1. 创建select对象
    SelectSvr select_svr;
    select_svr.AddFd(listen_sock); //将listen_sock添加到时间集合当中


    while(1)
    {
        vector<int> vec;
        int ret = select_svr.Select(&vec);
        if(ret < 0)
        {
            perror("select");
            return 0;
        }
        else if(ret == 0)
        {
            continue;
        }

        //正常监控到了文件描述符
        //就虚的文件描述符被放在了vec当中
        //只需要遍历整个vec，挨个处理，就会将所有的事件处理掉
        for(size_t i = 0; i < vec.size(); i++)
        {
            //需要判断，当前处理的文件描述符是侦听套接字还是新连接套接字
            if(vec[i] == listen_sock)
            {
                //侦听套接字
                struct sockaddr_in cli_addr;
                socklen_t cli_addrlen = sizeof(cli_addr);

                int newsockfd = accept(listen_sock, (struct sockaddr*)&cli_addr, &cli_addrlen);//接收新连接的返回值是新连接的套接字描述符
                if(newsockfd < 0)
                {
                    perror("accept");
                    continue;  //接收失败，继续接收连接
                }

                printf("accept new connect from client %s : %d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));

                //让select将新连接套接字也给监听起来
                select_svr.AddFd(newsockfd);
            }
            else 
            {
                //新连接的套接字
                //接收数据
                char buf[1024] = {0};
                ssize_t recv_size = recv(vec[i], buf, sizeof(buf) - 1, 0); //recv函数接收的是新连接的套接字newsockfd，返回值类型是ssize_t。最后一个参数0表示阻塞接收。
                if(recv_size < 0) //函数调用出错了
                {
                    perror("recv");
                    continue; //继续接收
                }
                else if(recv_size == 0) //表示对端关闭连接
                {
                    printf("peer close connet\n");

                    select_svr.DeletFd(vec[i]);
                    close(vec[i]); //对端把连接套接字关闭掉了，自己也把对应的连接套接字关闭
                    continue;  //再去接收新的连接
                }

                //下面就是recv_size > 0的情况，就是接收到的情况
                printf("buf is : %s\n", buf);

                //发送数据
                memset(buf, '\0', sizeof(buf));
                strcpy(buf,"i am server!!!\n"); //strcpy不是安全函数
                ssize_t send_size = send(vec[i], buf, strlen(buf), 0); //最后的0表示阻塞发送
                if(send_size <= 0)
                {
                    perror("send"); 
                    continue;
                }
                else 
                {
                    printf("发送的字节数是：%zu\n", send_size); //ssize_t 用 %zu 输出
                }
            }
        }
    }

    close(listen_sock);  //最后要把侦听套接字关掉

    return 0;
}
